package com.tomato.modules.id.strategy.impl;

import com.tomato.modules.id.po.Result;
import com.tomato.modules.id.po.ResultCode;
import com.tomato.modules.id.po.SegmentId;
import com.tomato.modules.id.strategy.IdGeneratorStrategyService;
import com.tomato.modules.id.service.SegmentIdService;
import com.tomato.modules.id.thread.ThreadPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutorService;

/**
 * 缓存 ID 生成
 *
 * @author lizhifu
 * @date 2022/3/18
 */
public class CachedIdGeneratorStrategy implements IdGeneratorStrategyService {
    private static final Logger logger = LoggerFactory.getLogger(CachedIdGeneratorStrategy.class);
    private ExecutorService executorService = ThreadPoolConfig.getCachedIdGeneratorStrategy();

    /**
     * 当前号段
     */
    protected volatile SegmentId current;
    /**
     * 下一个号段
     */
    protected volatile SegmentId next;
    /**
     * 业务类型
     */
    protected String bizType;
    /**
     * id 服务
     */
    protected SegmentIdService segmentIdService;
    /**
     * 是否正在加载下一个
     */
    private volatile boolean isLoadingNext;
    /**
     * 锁对象
     */
    private Object lock = new Object();

    /**
     * 构造函数
     * @param bizType
     * @param segmentIdService
     */
    public CachedIdGeneratorStrategy(String bizType, SegmentIdService segmentIdService) {
        this.bizType = bizType;
        this.segmentIdService = segmentIdService;
        init();
    }

    @Override
    public Long nextId() {
        while (true) {
            if (current == null) {
                init();
                logger.error("数据没有初始化:{}",bizType);
                continue;
            }
            Result result = current.nextId();
            // 一直循环到正常获取 ID
            if (result.getCode().equals(ResultCode.NORMAL)) {
                return result.getId();
            }
            if (result.getCode().equals(ResultCode.OVER)) {
                init();
            }
            if (result.getCode().equals(ResultCode.LOADING)) {
                loadNext();
                return result.getId();
            }
        }
    }

    /**
     * 防止初始化尚未完成，已经对外提供了服务
     */
    public synchronized void init() {
        if (current == null || !current.checkMaxId()) {
            // 1 应用启动初始化时进入
            // 2 极限的操作，例如线程池已经满了，此时直接去重新获取 next
            if(next == null){
                SegmentId segmentId = segmentIdService.updateMaxId(bizType);
                this.current = segmentId;
                logger.info("初始化bizType:{},segmentId:{}",bizType,segmentId);
            }else {
                logger.error("超过 maxId 不可用，检查步长是否不合理:{}",bizType);
                current = next;
                next = null;
            }
        }
    }
    public void loadNext() {
        if (next == null && !isLoadingNext) {
            synchronized (lock) {
                // 双重检测
                if (next == null && !isLoadingNext) {
                    isLoadingNext = true;
                    executorService.submit(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                // 无论获取下个segmentId成功与否，都要将isLoadingNext赋值为false
                                next = segmentIdService.updateMaxId(bizType);
                                logger.info("超过 loadingId 预加载完成:{}",bizType);
                            } finally {
                                isLoadingNext = false;
                            }
                        }
                    });
                }
            }
        }
    }
}
